local wortox_soul_common = require("prefabs/wortox_soul_common")
local wortox_decoy_assets = {
    Asset("ANIM", "anim/player_emote_extra.zip"),
    Asset("ANIM", "anim/player_emotes_dance2.zip"),
    Asset("ANIM", "anim/player_actions.zip"),
}

local wortox_decoy_prefabs = {
    "wortox_decoy_explode_fx",
    "wortox_decoy_fizzle_fx",
    "wortox_soul_spawn_fx",
}

local COMBAT_MUSTHAVE_TAGS = { "_combat", "_health" }
local COMBAT_CANTHAVE_TAGS = { "INLIMBO", "soul", "noauradamage", "companion" }

local function SetOwner_decoy(inst, decoyowner)
    inst.decoyowner = decoyowner
    if inst.components.follower then
        inst.components.follower:SetLeader(inst.decoyowner)
    end
    if inst.decoyowner == nil then
        inst._ownername:set("")
        return
    end
    inst._ownername:set(inst.decoyowner:GetDisplayName())

    local x, y, z = inst.Transform:GetWorldPosition()
    local ents = TheSim:FindEntities(x, y, z, TUNING.SKILLS.WORTOX.SOULDECOY_TAUNT_RADIUS, COMBAT_MUSTHAVE_TAGS,
        COMBAT_CANTHAVE_TAGS)
    for _, ent in ipairs(ents) do
        if ent:IsValid() and ent.entity:IsVisible() then
            if ent.components.combat:CanTarget(inst) then
                if wortox_soul_common.SoulDamageTest(inst, ent, decoyowner) then
                    ent.components.combat:SetTarget(inst)
                    if ent.components.combat:TargetIs(inst) then
                        inst.decoylured[ent] = true
                    end
                end
            end
        end
    end

    inst.decoyowner.wortox_decoy_inst = inst
    inst.Transform:SetRotation(inst.decoyowner.Transform:GetRotation())

    local duration = 10
    inst.decoythorns = nil
    inst.decoyexplodes = nil
    if inst.decoyowner:HasTag("player") then
        inst.components.skinner:CopySkinsFromPlayer(inst.decoyowner)
    else
        inst.components.skinner:SetupNonPlayerData()
    end
    inst.decoythorns = true
    inst.decoyexplodes = true

    inst.sg:GoToState("idle", { deathtime = GetTime() + duration })
end

local function OnDeath_decoy(inst)
    if inst.decoyowner and inst.decoyowner.wortox_decoy_inst == inst then
        inst.decoyowner.wortox_decoy_inst = nil
    end

    -- Add nearby things targeting the lure to the lured list.
    local decoyowner = inst.decoyowner and inst.decoyowner:IsValid() and inst.decoyowner or nil
    local x, y, z = inst.Transform:GetWorldPosition()
    local ents = TheSim:FindEntities(x, y, z, TUNING.SKILLS.WORTOX.SOULDECOY_TAUNT_RADIUS, COMBAT_MUSTHAVE_TAGS,
        COMBAT_CANTHAVE_TAGS)
    for _, ent in ipairs(ents) do
        if ent.components.combat then
            if ent.components.combat:TargetIs(inst) or (decoyowner and ent.components.combat:TargetIs(decoyowner)) then
                if wortox_soul_common.SoulDamageTest(inst, ent, decoyowner) then
                    inst.decoylured[ent] = true
                end
            end
        end
    end

    -- Redirect focus back on the owner this inst is now dead and combat target will drop.
    if decoyowner then
        for ent, _ in pairs(inst.decoylured) do
            if ent:IsValid() and ent.entity:IsVisible() then
                if (ent.components.combat and ent.components.combat:TargetIs(inst)) or ent.components.combat:TargetIs(decoyowner) then
                    if ent:GetDistanceSqToInst(decoyowner) <= PLAYER_CAMERA_SEE_DISTANCE_SQ then
                        ent.components.combat:SetTarget(decoyowner)
                    end
                end
            end
        end
    end
end
local AREAATTACK_MUST_TAGS = { "_combat" }
local excludetags_AreaAttack = { "INLIMBO", }
local function OnAttacked_decoy(inst, data)
    local ent = data and data.attacker or nil
    local decoyowner = inst.decoyowner and inst.decoyowner:IsValid() and inst.decoyowner or nil
    if ent and ent:IsValid() then
        if wortox_soul_common.SoulDamageTest(inst, ent, decoyowner) then
            inst.decoythornstarget = ent
        end
    end
    --替身被攻击会对5半径范围内生物减速80%并持续5秒
    local x, y, z = inst.Transform:GetWorldPosition()
    local ents = TheSim:FindEntities(x, y, z, 5, AREAATTACK_MUST_TAGS, excludetags_AreaAttack)
    local combat = decoyowner and decoyowner.components.combat
    for i, target in ipairs(ents) do
        if target.components.locomotor and decoyowner ~= target and inst ~= target and combat and combat:IsValidTarget(target) and
            combat:CanTarget(target) and not combat:IsAlly(target) then
            if target.doro_decoy_speedtask then
                target.doro_decoy_speedtask:Cancel()
            end
            target.components.locomotor:SetExternalSpeedMultiplier(target, "doro_decoy", 0.2)

            target.doro_decoy_speedtask = target:DoTaskInTime(5, function()
                if target.components.locomotor then
                    target.components.locomotor:RemoveExternalSpeedMultiplier(target, "doro_decoy")
                end
            end)
        end
    end
end

local function DoThorns_decoy(inst)
    local ent = inst.decoythornstarget
    if ent and ent:IsValid() and ent.entity:IsVisible() and
        ent:HasAllTags(COMBAT_MUSTHAVE_TAGS) and not ent:HasAnyTag(COMBAT_CANTHAVE_TAGS) and
        inst.components.combat:CanTarget(ent) then
        local initial_damage = inst.components.combat.defaultdamage

        local decoyowner = inst.decoyowner and inst.decoyowner:IsValid() and inst.decoyowner or nil
        local damage = initial_damage * TUNING.SKILLS.WORTOX.SOULDECOY_THORNS_DAMAGE_MULT
        if decoyowner then
            local souls_max = TUNING.SKILLS.WORTOX.SOUL_DAMAGE_MAX_SOULS
            local damage_percent = math.min(decoyowner.soulcount or 0, souls_max) / souls_max
            damage = damage * (1 + (TUNING.SKILLS.WORTOX.SOUL_DAMAGE_SOULS_BONUS_MULT - 1) * damage_percent)
            inst.components.combat:SetDefaultDamage(damage)
        end
        if wortox_soul_common.SoulDamageTest(inst, ent, decoyowner) then
            local x, y, z = ent.Transform:GetWorldPosition()
            local fx = SpawnPrefab("wortox_soul_spawn_fx")
            fx.Transform:SetPosition(x, y, z)
            if decoyowner then
                local damagetoent = damage
                local explosiveresist = ent.components.explosiveresist
                if explosiveresist then
                    damagetoent = damagetoent * (1 - explosiveresist:GetResistance())
                    explosiveresist:OnExplosiveDamage(damagetoent, decoyowner)
                end
                ent.components.combat:GetAttacked(decoyowner, damagetoent, nil, "soul")
            else
                inst.components.combat:DoAttack(ent)
            end
        end

        inst.components.combat:SetDefaultDamage(initial_damage)
    end
end

local function DoExplosion_decoy(inst)
    if inst.decoythorns then
        inst:DoThorns()
    end
    local decoyowner = inst.decoyowner and inst.decoyowner:IsValid() and inst.decoyowner or nil
    local damage = inst.components.combat.defaultdamage
    if decoyowner then
        local souls_max = TUNING.SKILLS.WORTOX.SOUL_DAMAGE_MAX_SOULS
        local damage_percent = math.min(decoyowner.soulcount or 0, souls_max) / souls_max
        damage = damage * (1 + (TUNING.SKILLS.WORTOX.SOUL_DAMAGE_SOULS_BONUS_MULT - 1) * damage_percent)
        inst.components.combat:SetDefaultDamage(damage)
    end
    local x, y, z = inst.Transform:GetWorldPosition()
    local ents = TheSim:FindEntities(x, y, z, TUNING.SKILLS.WORTOX.SOULDECOY_EXPLODE_RADIUS, COMBAT_MUSTHAVE_TAGS,
        COMBAT_CANTHAVE_TAGS)
    for _, ent in ipairs(ents) do
        if ent:IsValid() and ent.entity:IsVisible() then
            if inst.components.combat:CanTarget(ent) then
                local shouldharm = inst.decoylured[ent]
                if not shouldharm then
                    if ent.components.combat then
                        if ent.components.combat:TargetIs(inst) or decoyowner and ent.components.combat:TargetIs(decoyowner) then
                            if wortox_soul_common.SoulDamageTest(inst, ent, decoyowner) then
                                shouldharm = true
                            end
                        end
                    end
                end
                if shouldharm then
                    if decoyowner then
                        local damagetoent = damage
                        local explosiveresist = ent.components.explosiveresist
                        if explosiveresist then
                            damagetoent = damagetoent * (1 - explosiveresist:GetResistance())
                            explosiveresist:OnExplosiveDamage(damagetoent, decoyowner)
                        end
                        ent.components.combat:GetAttacked(decoyowner, damagetoent, nil, "soul")
                    else
                        inst.components.combat:DoAttack(ent)
                    end
                end
            end
        end
    end
end

local function DoFizzle_decoy(inst)
    if inst.decoythorns then
        inst:DoThorns()
    end
end

local function DisplayNameFn_decoy(inst)
    local ownername = inst._ownername:value()
    return ownername ~= "" and subfmt(STRINGS.NAMES.WORTOX_DECOY_FMT, { name = ownername }) or nil
end

local function GetSpecialDescription_decoy(inst, viewer)
    if not viewer:HasTag("playerghost") then
        local ownername = inst._ownername:value()
        if ownername ~= "" then
            local descriptions = GetString(viewer.prefab, "DESCRIBE", "WORTOX")
            local description = descriptions and descriptions.GENERIC or nil
            if description then
                return string.format(description, ownername) -- Bypass translations for player names.
            end
        end
    end
end

local function doro_decoy_fn()
    local inst = CreateEntity()

    inst.entity:AddTransform()
    inst.entity:AddAnimState()
    inst.entity:AddSoundEmitter()
    inst.entity:AddDynamicShadow()
    inst.entity:AddNetwork()

    inst:SetPhysicsRadiusOverride(.75) -- Same as characters for hitting them but no physics because this is nothing more than a decoy.

    inst.DynamicShadow:SetSize(2, 1)
    inst.Transform:SetFourFaced()

    inst.AnimState:AddOverrideBuild("player_emote_extra")
    inst.AnimState:AddOverrideBuild("player_actions")
    inst.AnimState:Hide("ARM_carry")

    inst.AnimState:SetBank("wilson")
    inst.AnimState:SetBuild("wilson")
    inst.AnimState:PlayAnimation("idle_loop", true)

    inst:AddTag("decoy")
    inst:AddTag("soulless")
    inst:AddTag("scarytoprey")

    inst._ownername = net_string(inst.GUID, "wortox_decoy._ownername")
    inst.displaynamefn = DisplayNameFn_decoy

    inst.entity:SetPristine()

    if not TheWorld.ismastersim then
        return inst
    end

    inst.persists = false
    inst.decoylured = {}

    local follower = inst:AddComponent("follower") -- For leader combat targets.
    follower.noleashing = true
    follower.keepdeadleader = true
    follower.keepleaderonattacked = true
    follower.keepleaderduringminigame = true
    follower.neverexpire = true

    inst:AddComponent("colouradder")
    local skinner = inst:AddComponent("skinner")
    skinner:SetupNonPlayerData()

    local inspectable = inst:AddComponent("inspectable")
    inspectable.getspecialdescription = GetSpecialDescription_decoy

    local health = inst:AddComponent("health")
    health:SetMaxHealth(1)
    health.nofadeout = true

    local combat = inst:AddComponent("combat")
    combat:SetDefaultDamage(TUNING.SKILLS.WORTOX.SOULDECOY_EXPLODE_DAMAGE)
    inst:ListenForEvent("attacked", OnAttacked_decoy)

    inst.SetOwner = SetOwner_decoy

    inst.OnDeath = OnDeath_decoy
    inst.DoThorns = DoThorns_decoy
    inst.DoExplosion = DoExplosion_decoy
    inst.DoFizzle = DoFizzle_decoy

    inst:SetStateGraph("SGwortox_decoy")

    inst:SetPrefabNameOverride("wortox_decoy")

    return inst
end

-----------------------------------------------------------------------------

return Prefab("doro_decoy", doro_decoy_fn, wortox_decoy_assets, wortox_decoy_prefabs)
